/*
 * Licensed Materials - Property of tenxcloud.com
 * (C) Copyright 2018 TenxCloud. All Rights Reserved.
 * 2018-06-19  @author lizhen
 */

package audit

import (
	"sync"
	"time"

	"github.com/jinzhu/gorm"
	"github.com/lithammer/shortuuid"
)

func NewModel(clusterID, namespace, url, clientIP, httpMethod, username, body string) *model {
	return &model{
		ID:          "AUDID-" + shortuuid.New()[:12],
		ClusterID:   clusterID,
		Namespace:   namespace,
		RequestBody: body,
		URL:         url,
		HttpMethod:  httpMethod,
		Username:    username,
		RequestedAt: time.Now(),
		inserted:    false,
		toBeUpdated: make(map[string]interface{}),
		rwLock:      &sync.Mutex{},
		ClientIP:    clientIP,
	}
}

type model struct {
	ID                    string                 `gorm:"column:id"`
	Namespace             string                 `gorm:"column:namespace"`
	ClusterID             string                 `gorm:"column:cluster_id"`
	OperationType         int                    `gorm:"column:operation_type"`
	ResourceType          int                    `gorm:"column:resource_type"`
	ResourceName          string                 `gorm:"column:resource_name"`
	RequestBody           string                 `gorm:"column:resource_config"`
	RequestedAt           time.Time              `gorm:"column:time"`
	DurationInMilliSecond int                    `gorm:"column:duration"`
	HttpCode              int                    `gorm:"column:status"`
	URL                   string                 `gorm:"column:url"`
	HttpMethod            string                 `gorm:"column:http_method"`
	ClientIP              string                 `orm:"column(client_ip)" json:"client_ip"`
	Username              string                 `gorm:"column:operator"`
	inserted              bool                   `gorm:"-" sql:"-"`
	toBeUpdated           map[string]interface{} `gorm:"-" sql:"-"`
	rwLock                *sync.Mutex
}

func (m *model) isInserted() bool {
	m.rwLock.Lock()
	defer m.rwLock.Unlock()
	return m.inserted
}

func (model) TableName() string {
	return "tenx_audit"
}

func (m *model) SetResourceName(detail string) {
	m.ResourceName = detail
	if m.isInserted() {
		m.toBeUpdated["resource_name"] = detail
	}
}

func (m *model) SetOperationType(operationType int) {
	m.OperationType = operationType
	if m.isInserted() {
		m.toBeUpdated["operation_type"] = operationType
	}
}

func (m *model) SetResourceType(resourceType int) {
	m.ResourceType = resourceType
	if m.isInserted() {
		m.toBeUpdated["resource_type"] = resourceType
	}
}

func (m *model) SetClusterID(id string) {
	m.ClusterID = id
	if m.isInserted() {
		m.toBeUpdated["cluster_id"] = id
	}
}

func (m *model) SetNamespace(space string) {
	m.Namespace = space
	if m.isInserted() {
		m.toBeUpdated["namespace"] = space
	}
}

func (m *model) SetHttpCode(code int) {
	m.HttpCode = code
	if m.isInserted() {
		m.toBeUpdated["status"] = code
	}
}

func (m *model) SetDuration() {
	m.DurationInMilliSecond = int((time.Now().UnixNano() - m.RequestedAt.UnixNano()) / int64(time.Millisecond))
	if m.isInserted() {
		m.toBeUpdated["duration"] = m.DurationInMilliSecond
	}
}

func (m *model) Insert(db *gorm.DB) error {
	m.rwLock.Lock()
	defer m.rwLock.Unlock()
	if err := db.Create(m).Error; err != nil {
		return err
	}
	m.inserted = true
	return nil
}

func (m *model) Update(db *gorm.DB) error {
	if len(m.toBeUpdated) <= 0 {
		return nil
	}
	if err := db.Model(m).Updates(m.toBeUpdated).Error; err != nil {
		return err
	}
	m.toBeUpdated = make(map[string]interface{})
	return nil
}
